package xcfg

import (
	"bytes"
	"fmt"
	"gitee.com/igolang/pkg/xutils"
	"github.com/pkg/errors"
	"github.com/spf13/viper"
	"io"
	"io/fs"
	"io/ioutil"
	"path/filepath"
)

const (
	defaultType = "yml"
	defaultFile = "config"
)

var v = &config{
	Viper:           viper.New(),
	in:              defaultType,
	defaultFileName: defaultFile,
}

type config struct {
	dir             string
	file            string
	in              string
	defaultFileName string
	*viper.Viper
}

func SetDir(dir string) {
	v.SetDir(dir)
}

func (c *config) SetDir(dir string) {
	c.dir = dir
}

func SetFile(file string) {
	v.SetFile(file)
}
func (c *config) SetFile(file string) {
	c.file = file
	ext := filepath.Ext(file)
	if len(ext) > 1 {
		c.SetType(ext[1:])
	}
}

func SetType(in string) {
	v.SetType(in)
}

func (c *config) SetType(in string) {
	c.in = in
}

func SetDefaultFileName(in string) {
	v.SetDefaultFileName(in)
}
func (c *config) SetDefaultFileName(in string) {
	c.defaultFileName = in
}

func GetType() string {
	return v.GetType()
}

func (c *config) GetType() string {
	return c.in
}

func GetDir() string {
	return v.GetDir()
}

func (c *config) GetDir() string {
	return c.dir
}

func GetFile() string {
	return v.GetFile()
}

func (c *config) GetFile() string {
	if c.file == "" {
		return ""
	}

	ext := filepath.Ext(c.file)
	if len(ext) > 1 {
		return c.file
	}

	return c.file + "." + c.in
}

func Init() error {
	return v.Init()
}
func (c *config) Init() error {
	file := c.GetFile()
	dir := c.GetDir()
	if file == "" && dir == "" {
		return c.error("config dir or file not exists")
	}
	c.SetConfigType(c.GetType())
	if file != "" {
		c.SetConfigFile(c.GetFile())
		return c.error(c.ReadInConfig())
	}

	df := filepath.Join(dir, c.defaultFileName+"."+c.GetType())
	if !xutils.FileExists(df) {
		return c.error(fmt.Sprintf("%s not exists", df))
	}
	c.SetConfigFile(df)
	if err := c.ReadInConfig(); err != nil {
		return c.error(err)
	}

	files := make([]string, 0)
	if err := filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {
		temp, _ := filepath.Split(path)
		if filepath.Dir(temp) != dir {
			return err
		}
		if info.IsDir() {
			return err
		}

		if info.Name() == "." || info.Name() == ".." {
			return err
		}

		if path == df {
			return err
		}

		if filepath.Ext(path) == ("." + c.GetType()) {
			files = append(files, path)
		}

		return err
	}); err != nil {
		return c.error(err)
	}

	if len(files) == 0 {
		return nil
	}
	// 将其他的文件合并到主配置文件
	vi := viper.New()
	for _, v := range files {
		if b, err := ioutil.ReadFile(v); err != nil && err != io.EOF {
			return c.error(err)
		} else {
			if len(b) == 0 {
				continue
			}
			vi.SetConfigType(c.GetType())
			if err := vi.ReadConfig(bytes.NewBuffer(b)); err != nil {
				return c.error(err)
			}
			prefix := c.filename(v)
			all := vi.AllSettings()
			if err := c.MergeConfigMap(map[string]interface{}{
				prefix: all,
			}); err != nil {
				return c.error(err)
			}
		}

	}

	return nil
}

func (c *config) filename(name string) string {
	_, name = filepath.Split(name)
	end := len(name) - len("."+c.GetType())
	return name[:end]
}

func (c *config) error(err interface{}) error {
	if err != nil {
		return errors.Errorf("xcfg: %v", err)
	}

	return nil
}
